/*
 * created by zsy, to get nvme inforation from nvme disk by passing kernel. 
 */

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

#include "libnvme/nvme.h"
#include "libnvme/nvme_info.h"
#include "../nvme/nvme_intel.h"
#include "../nvme/nvme_internal.h"


int nvme_admin_get_log_page(struct nvme_ctrlr *ctrlr, uint8_t log_page, uint32_t nsid, void *payload, uint32_t payload_size);
int get_intel_smart_log_page(struct nvme_ctrlr *ctrlr, struct spdk_nvme_intel_smart_information_page *page)
{
        int ret;

	ret = nvme_admin_get_log_page(ctrlr, NVME_INTEL_LOG_SMART, NVME_GLOBAL_NS_TAG,
					     page, sizeof(struct spdk_nvme_intel_smart_information_page));
        if(ret) {
		nvme_err("spdk_nvme_ctrlr_cmd_get_log_page() failed\n");
		return ret;
	}

	return 0;
}

int get_health_log_page(struct nvme_ctrlr *ctrlr, struct spdk_nvme_health_information_page *health_page)
{
        int ret;

	ret = nvme_admin_get_log_page(ctrlr, NVME_LOG_HEALTH_INFORMATION,
					     NVME_GLOBAL_NS_TAG, health_page, sizeof(*health_page));
        if(ret) {
		nvme_err("spdk_nvme_ctrlr_cmd_get_log_page() failed\n");
                return ret;
	}

	return 0;
}

static void print_uint_var_dec(uint8_t *array, unsigned int len)
{
	uint64_t result = 0;
	int i = len;

	while (i > 0) {
		result += (uint64_t)array[i - 1] << (8 * (i - 1));
		i--;
	}
	printf("%lu", result);
}

static void print_uint128_hex(uint64_t *v)
{
	unsigned long long lo = v[0], hi = v[1];
	if (hi) {
		printf("0x%llX%016llX", hi, lo);
	} else {
		printf("0x%llX", lo);
	}
}

static void print_uint128_dec(uint64_t *v)
{
	unsigned long long lo = v[0], hi = v[1];
	if (hi) {
		/* can't handle large (>64-bit) decimal values for now, so fall back to hex */
		print_uint128_hex(v);
	} else {
		printf("%llu", (unsigned long long)lo);
	}
}

static int print_smart_page(struct spdk_nvme_intel_smart_information_page *page)
{
	size_t i = 0;

		printf("Intel Health Information\n");
		printf("==================\n");
		for (i = 0;
		     i < SPDK_COUNTOF(page->attributes); i++) {
			if (page->attributes[i].code == NVME_INTEL_SMART_PROGRAM_FAIL_COUNT) {
				printf("Program Fail Count:\n");
				printf("  Normalized Value : %d\n",
				       page->attributes[i].normalized_value);
				printf("  Current Raw Value: ");
				print_uint_var_dec(page->attributes[i].raw_value, 6);
				printf("\n");
			}
			if (page->attributes[i].code == NVME_INTEL_SMART_ERASE_FAIL_COUNT) {
				printf("Erase Fail Count:\n");
				printf("  Normalized Value : %d\n",
				       page->attributes[i].normalized_value);
				printf("  Current Raw Value: ");
				print_uint_var_dec(page->attributes[i].raw_value, 6);
				printf("\n");
			}
			if (page->attributes[i].code == NVME_INTEL_SMART_WEAR_LEVELING_COUNT) {
				printf("Wear Leveling Count:\n");
				printf("  Normalized Value : %d\n",
				       page->attributes[i].normalized_value);
				printf("  Current Raw Value:\n");
				printf("  Min: ");
				print_uint_var_dec(&page->attributes[i].raw_value[0], 2);
				printf("\n");
				printf("  Max: ");
				print_uint_var_dec(&page->attributes[i].raw_value[2], 2);
				printf("\n");
				printf("  Avg: ");
				print_uint_var_dec(&page->attributes[i].raw_value[4], 2);
				printf("\n");
			}
			if (page->attributes[i].code == NVME_INTEL_SMART_E2E_ERROR_COUNT) {
				printf("End to End Error Detection Count:\n");
				printf("  Normalized Value : %d\n",
				       page->attributes[i].normalized_value);
				printf("  Current Raw Value: ");
				print_uint_var_dec(page->attributes[i].raw_value, 6);
				printf("\n");
			}
			if (page->attributes[i].code == NVME_INTEL_SMART_CRC_ERROR_COUNT) {
				printf("CRC Error Count:\n");
				printf("  Normalized Value : %d\n",
				       page->attributes[i].normalized_value);
				printf("  Current Raw Value: ");
				print_uint_var_dec(page->attributes[i].raw_value, 6);
				printf("\n");
			}
			if (page->attributes[i].code == NVME_INTEL_SMART_MEDIA_WEAR) {
				printf("Timed Workload, Media Wear:\n");
				printf("  Normalized Value : %d\n",
				       page->attributes[i].normalized_value);
				printf("  Current Raw Value: ");
				print_uint_var_dec(page->attributes[i].raw_value, 6);
				printf("\n");
			}
			if (page->attributes[i].code == NVME_INTEL_SMART_HOST_READ_PERCENTAGE) {
				printf("Timed Workload, Host Read/Write Ratio:\n");
				printf("  Normalized Value : %d\n",
				       page->attributes[i].normalized_value);
				printf("  Current Raw Value: ");
				print_uint_var_dec(page->attributes[i].raw_value, 6);
				printf("%%");
				printf("\n");
			}
			if (page->attributes[i].code == NVME_INTEL_SMART_TIMER) {
				printf("Timed Workload, Timer:\n");
				printf("  Normalized Value : %d\n",
				       page->attributes[i].normalized_value);
				printf("  Current Raw Value: ");
				print_uint_var_dec(page->attributes[i].raw_value, 6);
				printf("\n");
			}
			if (page->attributes[i].code == NVME_INTEL_SMART_THERMAL_THROTTLE_STATUS) {
				printf("Thermal Throttle Status:\n");
				printf("  Normalized Value : %d\n",
				       page->attributes[i].normalized_value);
				printf("  Current Raw Value:\n");
				printf("  Percentage: %d%%\n", page->attributes[i].raw_value[0]);
				printf("  Throttling Event Count: ");
				print_uint_var_dec(&page->attributes[i].raw_value[1], 4);
				printf("\n");
			}
			if (page->attributes[i].code == NVME_INTEL_SMART_RETRY_BUFFER_OVERFLOW_COUNTER) {
				printf("Retry Buffer Overflow Counter:\n");
				printf("  Normalized Value : %d\n",
				       page->attributes[i].normalized_value);
				printf("  Current Raw Value: ");
				print_uint_var_dec(page->attributes[i].raw_value, 6);
				printf("\n");
			}
			if (page->attributes[i].code == NVME_INTEL_SMART_PLL_LOCK_LOSS_COUNT) {
				printf("PLL Lock Loss Count:\n");
				printf("  Normalized Value : %d\n",
				       page->attributes[i].normalized_value);
				printf("  Current Raw Value: ");
				print_uint_var_dec(page->attributes[i].raw_value, 6);
				printf("\n");
			}
			if (page->attributes[i].code == NVME_INTEL_SMART_NAND_BYTES_WRITTEN) {
				printf("NAND Bytes Written:\n");
				printf("  Normalized Value : %d\n",
				       page->attributes[i].normalized_value);
				printf("  Current Raw Value: ");
				print_uint_var_dec(page->attributes[i].raw_value, 6);
				printf("\n");
			}
			if (page->attributes[i].code == NVME_INTEL_SMART_HOST_BYTES_WRITTEN) {
				printf("Host Bytes Written:\n");
				printf("  Normalized Value : %d\n",
				       page->attributes[i].normalized_value);
				printf("  Current Raw Value: ");
				print_uint_var_dec(page->attributes[i].raw_value, 6);
				printf("\n");
			}
		}
		printf("\n");

	return 0;
}

static int print_health_page(struct spdk_nvme_health_information_page *health_page)
{
	int i;
	printf("Health Information\n");
		printf("==================\n");

		printf("Critical Warnings:\n");
		printf("  Available Spare Space:     %s\n",
		       health_page->critical_warning.bits.available_spare ? "WARNING" : "OK");
		printf("  Temperature:               %s\n",
		       health_page->critical_warning.bits.temperature ? "WARNING" : "OK");
		printf("  Device Reliability:        %s\n",
		       health_page->critical_warning.bits.device_reliability ? "WARNING" : "OK");
		printf("  Read Only:                 %s\n",
		       health_page->critical_warning.bits.read_only ? "Yes" : "No");
		printf("  Volatile Memory Backup:    %s\n",
		       health_page->critical_warning.bits.volatile_memory_backup ? "WARNING" : "OK");
		printf("Current Temperature:         %u Kelvin (%d Celsius)\n",
		       health_page->temperature,
		       (int)health_page->temperature - 273);
		//printf("Temperature Threshold:       %u Kelvin (%d Celsius)\n",
		//       features[SPDK_NVME_FEAT_TEMPERATURE_THRESHOLD].result,
		//       (int)features[SPDK_NVME_FEAT_TEMPERATURE_THRESHOLD].result - 273);
		printf("Available Spare:             %u%%\n", health_page->available_spare);
		printf("Available Spare Threshold:   %u%%\n", health_page->available_spare_threshold);
		printf("Life Percentage Used:        %u%%\n", health_page->percentage_used);
		printf("Data Units Read:             ");
		print_uint128_dec(health_page->data_units_read);
		printf("\n");
		printf("Data Units Written:          ");
		print_uint128_dec(health_page->data_units_written);
		printf("\n");
		printf("Host Read Commands:          ");
		print_uint128_dec(health_page->host_read_commands);
		printf("\n");
		printf("Host Write Commands:         ");
		print_uint128_dec(health_page->host_write_commands);
		printf("\n");
		printf("Controller Busy Time:        ");
		print_uint128_dec(health_page->controller_busy_time);
		printf(" minutes\n");
		printf("Power Cycles:                ");
		print_uint128_dec(health_page->power_cycles);
		printf("\n");
		printf("Power On Hours:              ");
		print_uint128_dec(health_page->power_on_hours);
		printf(" hours\n");
		printf("Unsafe Shutdowns:            ");
		print_uint128_dec(health_page->unsafe_shutdowns);
		printf("\n");
		printf("Unrecoverable Media Errors:  ");
		print_uint128_dec(health_page->media_errors);
		printf("\n");
		printf("Lifetime Error Log Entries:  ");
		print_uint128_dec(health_page->num_error_info_log_entries);
		printf("\n");
		printf("Warning Temperature Time:    %u minutes\n", health_page->warning_temp_time);
		printf("Critical Temperature Time:   %u minutes\n", health_page->critical_temp_time);
		for (i = 0; i < 8; i++) {
			if (health_page->temp_sensor[i] != 0) {
				printf("Temperature Sensor %d:        %u Kelvin (%d Celsius)\n",
				       i + 1, health_page->temp_sensor[i],
				       (int)health_page->temp_sensor[i] - 273);
			}
		}
		printf("\n");

	return 0;
}

static unsigned long long nvme_info_strsize(unsigned long long val,
					    char *unit)
{
	unsigned long long uval = val;

	if (uval < 1024) {
		strcpy(unit, "");
		return uval;
	}

	uval /= 1024;
	if (uval < 1024) {
		strcpy(unit, "Ki");
		return uval;
	}

	uval /= 1024;
	if (uval < 1024) {
		strcpy(unit, "Mi");
		return uval;
	}

	uval /= 1024;
	strcpy(unit, "Gi");

	return uval;
}

static int nvme_info_ctrlr(struct nvme_ctrlr *ctrlr,
			   struct nvme_ctrlr_stat *cstat)
{
	struct nvme_register_data rdata;
	unsigned long long uval;
	char unit[16];

	/* Get information */
	if (nvme_ctrlr_stat(ctrlr, cstat) != 0) {
		fprintf(stderr, "Get controller info failed\n");
		return -1;
	}

	if (nvme_ctrlr_data(ctrlr, NULL, &rdata) != 0) {
		fprintf(stderr, "Get controller HW data failed\n");
		return -1;
	}

	printf("  Model name: %s\n", cstat->mn);
	printf("  Serial number: %s\n", cstat->sn);
	printf("  HW maximum queue entries: %u\n", rdata.mqes + 1);
	printf("  Maximum queue depth: %u\n", cstat->max_qd);

	uval = nvme_info_strsize(cstat->max_xfer_size, unit);
	printf("  Maximum request size: %llu %sB\n", uval, unit);

	return 0;
}

static int nvme_info_ns(struct nvme_ctrlr *ctrlr,
			struct nvme_ctrlr_stat *cstat)
{
	struct nvme_ns_stat nsstat;
	struct nvme_ns *ns;
	unsigned long long uval;
	char unit[16];
	unsigned int i;

	printf("%u namespaces:\n", cstat->nr_ns);

	for (i = 0; i < cstat->nr_ns; i++) {

		ns = nvme_ns_open(ctrlr, cstat->ns_ids[i]);
		if (!ns) {
			fprintf(stderr, "Open namespace %u failed\n",
				cstat->ns_ids[i]);
			return -1;
		}

		if (nvme_ns_stat(ns, &nsstat) != 0) {
			fprintf(stderr, "Get namespace %u info failed\n",
				cstat->ns_ids[i]);
				nvme_ns_close(ns);
			return -1;
		}

		uval = nvme_info_strsize(nsstat.sector_size * nsstat.sectors,
					 unit);
		printf("  Namespace %u/%u: %lu bytes sectors, %lu sectors (%llu %sB)\n",
		       nsstat.id, cstat->nr_ns,
		       nsstat.sector_size, nsstat.sectors,
		       uval, unit);

		nvme_ns_close(ns);

	}

	return 0;
}

static int nvme_info_qpair(struct nvme_ctrlr *ctrlr,
			   struct nvme_ctrlr_stat *cstat)
{
	struct nvme_qpair_stat *qpstat;
	struct nvme_qpair **qp;
	unsigned int i;
	int ret;

	printf("%u I/O queue pairs:\n", cstat->max_io_qpairs);

	qpstat = calloc(cstat->max_io_qpairs, sizeof(struct nvme_qpair_stat));
	qp = calloc(cstat->max_io_qpairs, sizeof(struct nvme_qpair *));
	if (!qpstat || !qp) {
		fprintf(stderr, "No memory for I/O qpairs info\n");
		return -1;
	}

	for (i = 0; i < cstat->max_io_qpairs; i++) {

		qp[i] = nvme_ioqp_get(ctrlr, 0, 0);
		if (!qp[i]) {
			fprintf(stderr, "Get I/O qpair %d failed\n", i);
			break;
		}

		ret = nvme_qpair_stat(qp[i], &qpstat[i]);
		if (ret) {
			fprintf(stderr,
				"Get I/O qpair %d information failed\n", i);
			break;
		}

		printf("  qpair %u/%u: ID %u, max qd %u, prio %u\n",
		       i + 1, cstat->max_io_qpairs,
		       qpstat[i].id, qpstat[i].qd, qpstat[i].qprio);

	}

	for (i = 0; i < cstat->max_io_qpairs; i++)
		if (qp[i])
			nvme_ioqp_release(qp[i]);

	free(qp);
	free(qpstat);

	return 0;
}

#ifdef __SELFT_APP__
int main(int argc, char **argv)
{
	struct nvme_ctrlr *ctrlr;
	struct nvme_ctrlr_stat cstat;
	int log_level = -1;
	char *dev;
	int i, ret;

	if (argc < 2) {
		printf("Usage: %s [options] <PCI device URL>\n"
		       "Options:\n"
		       "  -v : verbose mode (debug log levelNVME_LOG_NOTICE)\n",
		       argv[0]);
		exit(1);
	}

	for (i = 1; i < argc - 1; i++) {
		if (strcmp(argv[i], "-v") == 0) {
			log_level = NVME_LOG_DEBUG;
		} else {
			fprintf(stderr,
				"Unknown option \"%s\"\n",
			       argv[i]);
			exit(1);
		}
	}

	dev = argv[i];

	ret = nvme_lib_init(log_level, -1, NULL);
	if (ret != 0) {
		fprintf(stderr,
			"libnvme init failed %d (%s)\n",
			ret, strerror(-ret));
		exit(1);
	}

	printf("Opening NVMe controller %s\n", dev);
	ctrlr = nvme_ctrlr_open(dev, NULL);
	if (!ctrlr) {
		fprintf(stderr, "Open NVMe controller %s failed\n",
			dev);
		return -1;
	}

	ret = nvme_info_ctrlr(ctrlr, &cstat);
	if (ret != 0)
		goto out;

	{/*for health information*/
		struct spdk_nvme_intel_smart_information_page page;

		printf("getting smart log:\r\n");
		ret = get_intel_smart_log_page(ctrlr, &page);
		if (ret != 0)
			goto out;

		print_smart_page(&page);

		struct spdk_nvme_health_information_page health_page;
		ret = get_health_log_page(ctrlr, &health_page);
		if (ret != 0)
			goto out;
			
		print_health_page(&health_page);
	}

	ret = nvme_info_ns(ctrlr, &cstat);
	if (ret != 0)
		goto out;

	ret = nvme_info_qpair(ctrlr, &cstat);

 out:
	nvme_ctrlr_close(ctrlr);

	return ret;
}

#endif